package com.dream.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dream.base.redis.service.RedisService;
import com.dream.core.constant.Constants;
import com.dream.core.utils.StringUtils;
import com.dream.core.utils.uuid.IdUtils;
import com.dream.gateway.config.TokenConfig;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import com.dream.core.domain.*;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 网关鉴权
 *
 * @author ruoyi
 */
@Component
public class RequestAuthFilter implements GlobalFilter, Ordered {
    private static final Logger log = LoggerFactory.getLogger(RequestAuthFilter.class);

    private final static long EXPIRE_TIME = Constants.TOKEN_EXPIRE * 60;

    // 排除过滤的 uri 地址，nacos自行添加
    private List<String> ignoreWhiteList;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedisService redisService;

    @Autowired
    TokenConfig tokenConfig;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String url = exchange.getRequest().getURI().getPath();
        //1 认证服务所有放行,可放在白名单里
        if (StringUtils.isMatch("/oauth/**", url) || url.startsWith("/auth/login") || url.startsWith("/gateway")) {
            return chain.filter(exchange);
        }
        // 跳过不需要验证的路径
        if (StringUtils.matches(url, ignoreWhiteList)) {
            return chain.filter(exchange);
        }
        String token = getToken(exchange.getRequest());
        if (StringUtils.isBlank(token)) {
            return setUnauthorizedResponse(exchange, "令牌不能为空");
        }
        //验签
        Claims claims = parseToken(token);
        // 解析对应的权限以及用户信息
        String userKey = (String) claims.get(Constants.JWT_USER_TOKEN);
        if (StringUtils.isNull(userKey)) {
            return setUnauthorizedResponse(exchange, "登录状态已过期");
        }
        String userStr = redisTemplate.opsForValue().get(userKey);
        JSONObject cacheUserModel = JSONObject.parseObject(userStr);
        if (cacheUserModel == null || StringUtils.isBlank(cacheUserModel.getString("username"))) {
            return setUnauthorizedResponse(exchange, "令牌验证失败");
        }
        String user = cacheUserModel.getString("user");
        String roles = cacheUserModel.getString("roles");//list
        String permissions = cacheUserModel.getString("permissions");//Set
        JSONObject userModel = JSONObject.parseObject(user);
        String userId = userModel.getString("userId");
        String userName = userModel.getString("userName");
        // 设置过期时间
        redisService.expire(userKey, EXPIRE_TIME);
        // 设置用户信息到请求
        ServerHttpRequest mutableReq = exchange.getRequest().mutate().headers(n -> {
            n.add(tokenConfig.getHeader(), token);
            n.add("tan", "123tan");
            n.add(Constants.JWT_USERID, userId);
            n.add(Constants.JWT_USERNAME, urlEncode(userName));
            n.set("referer", exchange.getRequest().getURI().toString());
        }).build();
        ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();
        return chain.filter(mutableExchange);
    }

    private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange, String msg) {
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        response.setStatusCode(HttpStatus.OK);

        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());

        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            return bufferFactory.wrap(JSON.toJSONBytes(R.fail(HttpStatus.UNAUTHORIZED.value(), msg)));
        }));
    }

    /**
     * 获取请求token
     */
    private String getToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst(Constants.HEADER);
        if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) {
            token = token.replace(Constants.TOKEN_PREFIX, "");
        }
        return token;
    }

    /**
     * 从令牌中获取数据声明
     *
     * @param token 令牌
     * @return 数据声明
     */
    private Claims parseToken(String token) {
        return Jwts.parser()
                .setSigningKey(tokenConfig.getSecret().getBytes(StandardCharsets.UTF_8))
                //.setSigningKey(secret)
                .parseClaimsJws(token)
                //.parseClaimsJwt(token)
                .getBody();
    }

    /**
     * 内容编码
     *
     * @param str 内容
     * @return 编码后的内容
     */
    public static String urlEncode(String str) {
        try {
            return URLEncoder.encode(str, Constants.UTF8);
        } catch (UnsupportedEncodingException e) {
            return "";
        }
    }

    @Override
    public int getOrder() {
        return 100;
    }
}